package com.audaque.springboot.foshanupload.security.config;

import com.audaque.springboot.foshanupload.authcore.properties.LoginProperties;
import com.audaque.springboot.foshanupload.core.component.ApplicationContextProvider;
import com.audaque.springboot.foshanupload.web.properties.IgnoreResourcePathProperties;
import com.audaque.springboot.foshanupload.core.properties.SwitchProperties;
import com.audaque.springboot.foshanupload.security.encoder.MyPasswordEncoder;
import com.audaque.springboot.foshanupload.security.service.impl.MyUserDetailService;
import com.audaque.springboot.foshanupload.security.valid.filter.LoginFilter;
import com.audaque.springboot.foshanupload.security.valid.provider.UserVerifyAuthenticationProvider;
import com.audaque.springboot.foshanupload.security.valid.rsp.expire.InvalidSessionHandler;
import com.audaque.springboot.foshanupload.security.valid.rsp.kid.SessionInformationExpiredHandler;
import com.audaque.springboot.foshanupload.security.valid.rsp.login.MyAuthenticationFailureHandler;
import com.audaque.springboot.foshanupload.security.valid.rsp.login.MyAuthenticationSuccessHandler;
import com.audaque.springboot.foshanupload.security.valid.rsp.logout.MyLogoutSuccessHandler;
import com.audaque.springboot.foshanupload.security.valid.rsp.validfail.MyAccessDeniedHandler;
import com.audaque.springboot.foshanupload.security.valid.rsp.validfail.MyAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsUtils;

import java.util.List;

/**
 * @author zgb
 * @desc ...
 * @date 2022-08-10 22:55:48
 */

@Configuration
@EnableWebSecurity
//启用spring方法级安全时，使用这个注解
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;


    @Autowired
    private MyLogoutSuccessHandler myLogoutSuccessHandler;


    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;


    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;


    @Autowired
    private MyAuthenticationEntryPoint myAuthenticationEntryPoint;


    @Autowired
    private MyPasswordEncoder myPasswordEncoder;

    @Autowired
    private MyUserDetailService myUserDetailService;


    @Autowired
    private UserVerifyAuthenticationProvider authenticationManager;//认证用户类

    @Autowired
    private LoginProperties loginProperties;

    @Autowired
    private IgnoreResourcePathProperties ignoreResourcePathProperties;

    @Autowired
    private ApplicationContextProvider applicationContextProvider;

    /**
     * 超时处理
     */
    @Autowired
    private InvalidSessionHandler invalidSessionHandler;
    /**
     * 顶号处理
     */
    @Autowired
    private SessionInformationExpiredHandler sessionInformationExpiredHandler;

    @Autowired
    private SwitchProperties switchProperties;



    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++==*/


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailService).passwordEncoder(myPasswordEncoder);
    }


    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++==*/


    /**
     * configure(WebSecurity web)不走过滤链的放行
     * 即不通过security 完全对外的/最大级别的放行
     **/

    /*用于影响全局安全性(配置资源，设置调试模式，通过实现自定义防火墙定义拒绝请求)的配置设置。
    一般用于配置全局的某些通用事物，例如静态资源等
    */
    // 直接放行了  如果是login接口 必须通过HttpSecurity 走过滤链 因为登录涉及 SecurityContextHolder
    @Override
    public void configure(WebSecurity web) throws Exception {
        //纯json服务跳过
        Boolean onlyJson = switchProperties.getOnlyJson();
        if (!onlyJson) {
            //允许对于网站静态资源的无授权访问
            //要排除的路径
            List<String> excludePathPatterns = ignoreResourcePathProperties.getList();
            String[] arr = excludePathPatterns.toArray(new String[excludePathPatterns.size()]);
            web.ignoring().antMatchers(arr);
            super.configure(web);
        }

    }


    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        //认证方式1
        httpSecurity
                // 登录行为由自己实现，参考 AuthController#login
                .formLogin()

                .and()
                .authorizeRequests()


                .antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求
                .permitAll()


                .requestMatchers(CorsUtils::isPreFlightRequest)
                .permitAll()


                //允许对于网站静态资源的无授权访问
                //要排除的路径
                .antMatchers(ignoreResourcePathProperties.getList().toArray(new String[ignoreResourcePathProperties.getList().size()])).permitAll()

                // 对于登录login 验证码captchaImage 允许匿名访问
                .antMatchers(loginProperties.getLoginUrl(), loginProperties.getRegisterUrl(), "/redisOpt/redisTemplateTest").permitAll()
                //其他所有请求需要登录, anyRequest 不能配置在 antMatchers 前面，一定要配置 .anyRequest().authenticated()，否则全部放行
                .anyRequest().authenticated()

                .and()
                //注入异常处理过滤器
                .exceptionHandling()
                //鉴权失败处理器
                .accessDeniedHandler(myAccessDeniedHandler)
                //鉴证失败处理器
                .authenticationEntryPoint(myAuthenticationEntryPoint)
                .and()
                .logout()
                /**
                 * The URL that triggers log out to occur (default is "/ logout"). If CSRF protection is enabled (default), then the request must also be a POST. This means that by default POST "/ logout" is required to trigger a log out. If CSRF protection is disabled, then any HTTP method is allowed.
                 * It is considered best practice to use an HTTP POST on any action that changes state (i. e. log out) to protect against CSRF attacks . If you really want to use an HTTP GET, you can use logoutRequestMatcher(new AntPathRequestMatcher(logoutUrl, "GET"));
                 */
                //.logoutUrl(loginProperties.getLogoutUrl()) // 你的登出URL
                .logoutRequestMatcher(new AntPathRequestMatcher(loginProperties.getLogoutUrl(), "GET"))
                .logoutSuccessHandler(myLogoutSuccessHandler) // 指定自定义handler
                .permitAll()
                .and()
                //配置登录过滤器
                .addFilterBefore(new LoginFilter(authenticationManager, myAuthenticationSuccessHandler, myAuthenticationFailureHandler, loginProperties.getLoginUrl()), UsernamePasswordAuthenticationFilter.class)
                .csrf().disable()
                // 禁用请求缓存,从而不再存储请求的URL。这意味着用户在登录后不会被重定向到之前访问的页面
                .requestCache().disable()
                .headers().disable()
                // 开启跨域
                .cors().disable()
                // 禁止允许iframe 嵌套
                .httpBasic().disable()
        // 禁用 AnonymousAuthenticationFilter
        //.anonymous().disable()
        ;


    }


}
